qtre.material = function(name, program){
	
	this.name = name;

	this._uniforms = [];

	this._renderableQueue = [];

	this._renderableNum = 0;

	this._program = program;

	this._camera = null;

	this._onRenderBegins = [];

	this._onRenderEnds = [];
}

qtre.material.prototype = {
	addRenderBeginCallback : function(callback){
		this._onRenderBegins.push(callback);
	},

	removeRenderBeginCallback : function(callback){
		for(var i = 0;  i < this._onRenderBegins.length; i++){
			if(this._onRenderBegins[i] == callback){
				delete this._onRenderBegins[i];
			}
		}
	},

	addRenderEndCallback : function(callback){
		this._onRenderEnds.push(callback);
	},

	removeRenderEndCallback : function(callback){
		for(var i = 0;  i < this._onRenderEnds.length; i++){
			if(this._onRenderEnds[i] == callback){
				delete this._onRenderEnds[i];
			}
		}
	},

	onRenderBegin : function(){
		for(var i = 0;  i < this._onRenderBegins.length; i++){
			this._onRenderBegins[i].call(this);
		}
	},

	onRenderEnd : function(){
		for(var i = 0;  i < this._onRenderEnds.length; i++){
			this._onRenderEnds[i].call(this);
		}
	},
	
	updateUniforms : function(uniforms){
		for(var uniform in uniforms){
			this._uniforms[uniform] = uniforms[uniform];
		}
	},

	hasUniform : function(name){
		return qtre.core.gl.getUniformLocation(this._program.program, name) != -1;
	},

	setUniforms : function(uniforms){
		this._uniforms = uniforms;
	},

	getUniform : function(name){
		if(!qtre.core.isset(name)){
			return this._uniforms;
		}
		if(!qtre.core.isset(this._uniforms[name])){
			throw("uniform "+name+" not exist in renderable "+this.name);
		}
		return this._uniforms[name];
	},

	setProgram : function(program){
		this._program = program;
	},

	getProgram : function(){
		return this._program;
	},

	getRenderables : function(){
		return this._renderableQueue;
	},

	getRenderable : function(name){
		return this._renderableQueue[name];
	},

	addRenderable : function(renderable){
		this._renderableQueue[renderable.name] = renderable;
		this._renderableNum++;
	},

	removeRenderable : function(renderable){
		if(typeof(renderable) == "string"){
			delete this._renderableQueue[renderable];
		}else{
			delete this._renderableQueue[renderable.name];
		}
		this._renderableNum--;
	},

	getRenderableNum : function(){
		return this._renderableNum;
	},

	
	bindCamera : function(camera){
		camera.renderQueue[this.name] = this;
		this._camera = camera;
	},

	unbindCamera : function(){
		delete camera.renderQueue[this.name];
		this._camera = null;
	},

	getCamera : function(){
		return this._camera;
	},

	bind : function(){
		this._program.use();
	},

	applyUniforms : function(){
		for(var name in this._uniforms){
			this._program.setUniform(name, this._uniforms[name]);
		}
	}
}

qtre.commonMaterials = {};

qtre.commonMaterials.phong = function(name){
	var program = tdl.programs.loadProgram("\n\
		uniform mat4 worldViewProjection;\n\
		uniform vec3 lightWorldPos;\n\
		uniform mat4 world;\n\
		uniform mat4 viewInverse;\n\
		uniform mat4 worldInverseTranspose;\n\
		attribute vec4 position;\n\
		attribute vec3 normal;\n\
		attribute vec2 texCoord;\n\
		varying vec4 v_position;\n\
		varying vec2 v_texCoord;\n\
		varying vec3 v_normal;\n\
		varying vec3 v_surfaceToLight;\n\
		varying vec3 v_surfaceToView;\n\
		void main() {\n\
			v_texCoord = texCoord;\n\
			v_position = (worldViewProjection * position);\n\
			v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\n\
			v_surfaceToLight = lightWorldPos - (world * position).xyz;\n\
			v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\n\
			gl_Position = v_position;\n\
		}",
		"\n\
		#ifdef GL_ES\n\
		precision highp float;\n\
		#endif\n\
		uniform vec4 lightColor;\n\
		varying vec4 v_position;\n\
		varying vec2 v_texCoord;\n\
		varying vec3 v_normal;\n\
		varying vec3 v_surfaceToLight;\n\
		varying vec3 v_surfaceToView;\n\
		\n\
		uniform sampler2D diffuseSampler;\n\
		uniform vec4 specular;\n\
		uniform sampler2D bumpSampler;\n\
		uniform float shininess;\n\
		\n\
		vec4 lit(float l ,float h, float m) {\n\
			return vec4(1.0,\n\
					  max(l, 0.0),\n\
					  (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\n\
					  1.0);\n\
		}\n\
		void main() {\n\
		  vec4 diffuse = texture2D(diffuseSampler, v_texCoord);\n\
		  vec3 normal = normalize(v_normal);\n\
		  vec3 surfaceToLight = normalize(v_surfaceToLight);\n\
		  vec3 surfaceToView = normalize(v_surfaceToView);\n\
		  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\n\
		  vec4 litR = lit(dot(normal, surfaceToLight),\n\
							dot(normal, halfVector), shininess);\n\
		  gl_FragColor = vec4((\n\
		  lightColor * (diffuse * litR.y\n\
								+ specular * litR.z)).rgb,\n\
			  diffuse.a);\n\
			//gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n\
		}");

	qtre.material.call(this, name, program);
	
	this.addRenderBeginCallback(function(){
		this.updateUniforms({
			lightWorldPos : [0, 100, 100],
			specular : [1, 1, 1, 1],
			lightColor : [1, 1, 1, 1]
		})
	})
}

qtre.commonMaterials.phong.prototype = new qtre.material();